/* 
 * File:   Stack.cpp
 * Author: Gab'z
 * 
 * Created on 27 mai 2012, 17:43
 */

#include "Stack.h"
#include <string>

namespace calculator {
    Logger* Stack::log = LoggerManager::getInstance()->getLogger("Stack");

    Stack::Stack() : Observable() {
    }

    Stack::~Stack() {
        for (std::list<const Constant*>::iterator it = _constantStack.begin();
                it != _constantStack.end();
                it++) {
            delete *it;
        }
        _constantStack.clear();
    }

    void Stack::push(const Constant* c) {
        _constantStack.push_front(c->clone());
        notify();
    }

    const Constant* Stack::top() const {
        return (!_constantStack.empty()) ? _constantStack.front() : 0;
    }

    void Stack::pop() {
        if (!_constantStack.empty()) {
            delete _constantStack.front();
            _constantStack.pop_front();
        }
        notify();
    }

    void Stack::clear() {
        for (std::list<const Constant*>::iterator it = _constantStack.begin();
                it != _constantStack.end();
                it++) {
            delete *it;
        }
        _constantStack.clear();
        notify();
    }

    Stack* Stack::clone() const {
        Stack* stack = new Stack();
        for (std::list <const Constant*>::const_iterator it = _constantStack.begin();
                it != _constantStack.end();
                it++) {
            stack->_constantStack.push_back((*it)->clone());
        }
        return stack;
    }

    bool Stack::empty() const {
        return _constantStack.empty();
    }

    unsigned int Stack::size() const {
        return _constantStack.size();
    }

    const Constant* Stack::getConstant(unsigned int n) const throw (CommandException) {
        unsigned int i = 0;
        for (std::list <const Constant*>::const_iterator it = _constantStack.begin();
                it != _constantStack.end();
                it++) {
            if (n == i) {
                return *it;
            }
            i++;
        }
        std::ostringstream oss;
        oss << "getConstant : Not enough Constant in Stack (stackSize : ";
        oss << _constantStack.size();
        oss << " / request : ";
        oss << n;
        oss << ")";
        throw CommandException(oss.str());
    }

    void Stack::swapTwoFirstConstant() {
        log->trace("Swapping two first constant");
        if (_constantStack.size() >= 2) {
            _constantStack.push_back(top());
            _constantStack.pop_front();
        }
        notify();
    }

    void Stack::swapAnyConstant(const unsigned int index1, const unsigned int index2) throw (ContextException) {
        std::ostringstream oss;
        oss << "Swapping any constant (index1 = " << index1 << " & index2 = " << index2 << ")";
        log->trace(oss.str());
        std::ostringstream oss2;
        oss2 << "Stack.size() = " << size();
        log->trace(oss2.str());
        // on supprime les 2 premiers éléments (les positions)
        _constantStack.pop_front();
        _constantStack.pop_front();

        // on copie les éléments pointés
        const Constant* a = getConstant(index1 - 1)->clone();
        const Constant* b = getConstant(index2 - 1)->clone();
        // on échange
        // place a en b
        try {
            setConstant(a, index2 - 1);
        } catch (ContextException& e) {
            throw;
        }
        // place b en a
        try {
            setConstant(b, index1 - 1);
        } catch (ContextException& e) {
            throw;
        }
        notify();
    }

    void Stack::setConstant(const Constant* constant, unsigned int n) throw (ContextException) {
        unsigned int i = 0;

        std::list <const Constant*>::iterator it = _constantStack.begin();
        while (i != n && it != _constantStack.end()) {
            it++;
            i++;
        }
        if (it == _constantStack.end()) {
            std::ostringstream oss;
            oss << "setConstant : Not enough Constant in Stack (stackSize : ";
            oss << _constantStack.size();
            oss << " / request : ";
            oss << n;
            oss << ")";
            throw ContextException(oss.str());
        }
        delete *it;
        *it = constant;
    }
}
